use std::ptr;
use std::ptr::null_mut;
use std::sync::atomic::{AtomicUsize, Ordering};
macro_rules! alloc_test_assert {
    ($ expr : expr) => {{
        NUM_ASSERT.fetch_add(1, Ordering::SeqCst);
        if !$expr {
            panic!("Assertion failed: {}", stringify!($expr));
        }
    }};
}
static LIST_NULL: *mut std::ffi::c_void = ptr::null_mut();
static NUM_ASSERT: AtomicUsize = AtomicUsize::new(0);
type UnitTestFunction = fn();
#[repr(C)]
struct ListEntry {}
#[repr(C)]
struct ListIterator {
    prev_next: *mut *mut ListEntry,
    current: *mut ListEntry,
}
#[allow(dead_code)]
type ListValue = *mut std::ffi::c_void;
#[allow(dead_code)]
type ListCompareFunc = fn(ListValue, ListValue) -> i32;
#[allow(dead_code)]
type ListEqualFunc = fn(ListValue, ListValue) -> i32;
static mut VARIABLE1: i32 = 50;
#[allow(dead_code)]
static TESTS: [Option<UnitTestFunction>; 14] = [
    Some(test_list_append),
    Some(test_list_prepend),
    Some(test_list_free),
    Some(test_list_next),
    Some(test_list_nth_entry),
    Some(test_list_nth_data),
    Some(test_list_length),
    Some(test_list_remove_entry),
    Some(test_list_remove_data),
    Some(test_list_sort),
    Some(test_list_find_data),
    Some(test_list_to_array),
    Some(test_list_iterate),
    Some(test_list_iterate_bad_remove),
];
fn generate_list() -> *mut ListEntry {
    let list: *mut ListEntry = std::ptr::null_mut();
    unsafe {
        assert!(!list_append(&list, &variable1).is_null());
        assert!(!list_append(&list, &variable2).is_null());
        assert!(!list_append(&list, &variable3).is_null());
        assert!(!list_append(&list, &variable4).is_null());
    }
    list
}
fn check_list_integrity(mut list: *mut ListEntry) {
    let mut prev: *mut ListEntry = ptr::null_mut();
    let mut rover = list;
    while !rover.is_null() {
        assert!(list_prev(rover) == prev);
        prev = rover;
        rover = list_next(rover);
    }
}
#[test]
fn test_list_append() {
    let mut list: Option<Rc<RefCell<ListEntry>>> = None;
    assert!(list_append(&mut list, &mut variable1) != None);
    check_list_integrity(&list);
    assert!(list_append(&mut list, &mut variable2) != None);
    check_list_integrity(&list);
    assert!(list_append(&mut list, &mut variable3) != None);
    check_list_integrity(&list);
    assert!(list_append(&mut list, &mut variable4) != None);
    check_list_integrity(&list);
    assert!(list_length(list.clone()) == 4);
    assert!(list_nth_data(list.clone(), 0) == &mut variable1);
    assert!(list_nth_data(list.clone(), 1) == &mut variable2);
    assert!(list_nth_data(list.clone(), 2) == &mut variable3);
    assert!(list_nth_data(list.clone(), 3) == &mut variable4);
    alloc_test_set_limit(0);
    assert!(list_length(list.clone()) == 4);
    assert!(list_append(&mut list, &mut variable1) == None);
    assert!(list_length(list.clone()) == 4);
    check_list_integrity(&list);
    list_free(list);
}
#[test]
fn test_list_prepend() {
    unsafe {
        let mut list: *mut ListEntry = ptr::null_mut();
        assert!(!list_prepend(&mut list, &mut VARIABLE1 as *mut _ as *mut c_void).is_null());
        check_list_integrity(list);
        assert!(!list_prepend(&mut list, &mut VARIABLE2 as *mut _ as *mut c_void).is_null());
        check_list_integrity(list);
        assert!(!list_prepend(&mut list, &mut VARIABLE3 as *mut _ as *mut c_void).is_null());
        check_list_integrity(list);
        assert!(!list_prepend(&mut list, &mut VARIABLE4 as *mut _ as *mut c_void).is_null());
        check_list_integrity(list);
        assert!(list_nth_data(list, 0) == &mut VARIABLE4 as *mut _ as *mut c_void);
        assert!(list_nth_data(list, 1) == &mut VARIABLE3 as *mut _ as *mut c_void);
        assert!(list_nth_data(list, 2) == &mut VARIABLE2 as *mut _ as *mut c_void);
        assert!(list_nth_data(list, 3) == &mut VARIABLE1 as *mut _ as *mut c_void);
        alloc_test_set_limit(0);
        assert!(list_length(list) == 4);
        assert!(list_prepend(&mut list, &mut VARIABLE1 as *mut _ as *mut c_void).is_null());
        assert!(list_length(list) == 4);
        check_list_integrity(list);
        list_free(list);
    }
}
#[test]
fn test_list_free() {
    use std::ptr;
    let list = generate_list();
    list_free(list);
    list_free(ptr::null_mut());
}
#[test]
fn test_list_next() {
    let mut list = generate_list();
    let mut rover = list.as_mut();
    assert_eq!(list_data(rover), &variable1);
    rover = list_next(rover);
    assert_eq!(list_data(rover), &variable2);
    rover = list_next(rover);
    assert_eq!(list_data(rover), &variable3);
    rover = list_next(rover);
    assert_eq!(list_data(rover), &variable4);
    rover = list_next(rover);
    assert!(rover.is_none());
    list_free(list);
}
#[test]
fn test_list_nth_entry() {
    let list = generate_list();
    unsafe {
        let mut entry = list_nth_entry(list, 0);
        assert_eq!(list_data(entry), &variable1);
        entry = list_nth_entry(list, 1);
        assert_eq!(list_data(entry), &variable2);
        entry = list_nth_entry(list, 2);
        assert_eq!(list_data(entry), &variable3);
        entry = list_nth_entry(list, 3);
        assert_eq!(list_data(entry), &variable4);
        entry = list_nth_entry(list, 4);
        assert!(entry.is_null());
        entry = list_nth_entry(list, 400);
        assert!(entry.is_null());
    }
    list_free(list);
}
#[test]
fn test_list_nth_data() {
    let list = generate_list();
    assert!(list_nth_data(&list, 0) == &variable1);
    assert!(list_nth_data(&list, 1) == &variable2);
    assert!(list_nth_data(&list, 2) == &variable3);
    assert!(list_nth_data(&list, 3) == &variable4);
    assert!(list_nth_data(&list, 4).is_none());
    assert!(list_nth_data(&list, 400).is_none());
    list_free(list);
}
#[test]
fn test_list_length() {
    let mut list: *mut ListEntry;
    list = unsafe { generate_list() };
    assert!(unsafe { list_length(list) } == 4);
    assert!(unsafe { list_prepend(&mut list, &variable1) } != std::ptr::null_mut());
    assert!(unsafe { list_length(list) } == 5);
    unsafe {
        list_free(list);
    }
    assert!(unsafe { list_length(std::ptr::null_mut()) } == 0);
}
#[test]
fn test_list_remove_entry() {
    let mut empty_list: *mut ListEntry = null_mut();
    let mut list: *mut ListEntry;
    let mut entry: *mut ListEntry;
    unsafe {
        list = generate_list();
        entry = list_nth_entry(list, 2);
        assert!(list_remove_entry(&mut list, entry) != 0);
        assert!(list_length(list) == 3);
        check_list_integrity(list);
        entry = list_nth_entry(list, 0);
        assert!(list_remove_entry(&mut list, entry) != 0);
        assert!(list_length(list) == 2);
        check_list_integrity(list);
        assert!(list_remove_entry(&mut list, null_mut()) == 0);
        assert!(list_remove_entry(&mut empty_list, null_mut()) == 0);
        list_free(list);
        list = null_mut();
        assert!(list_append(&mut list, &variable1 as *const _ as *mut _) != null_mut());
        assert!(list != null_mut());
        assert!(list_remove_entry(&mut list, list) != 0);
        assert!(list == null_mut());
        list = generate_list();
        entry = list_nth_entry(list, 3);
        assert!(list_remove_entry(&mut list, entry) != 0);
        check_list_integrity(list);
        list_free(list);
    }
}
#[test]
fn test_list_remove_data() {
    let mut entries = vec![89, 4, 23, 42, 4, 16, 15, 4, 8, 99, 50, 30, 4];
    let num_entries = entries.len();
    let mut val: i32;
    let mut list: *mut ListEntry = std::ptr::null_mut();
    let mut i: usize;
    for i in 0..num_entries {
        assert!(!list_prepend(&mut list, &mut entries[i]).is_null());
    }
    val = 0;
    assert!(list_remove_data(&mut list, int_equal, &mut val) == 0);
    val = 56;
    assert!(list_remove_data(&mut list, int_equal, &mut val) == 0);
    check_list_integrity(list);
    val = 8;
    assert!(list_remove_data(&mut list, int_equal, &mut val) == 1);
    assert!(list_length(list) == (num_entries - 1) as _);
    check_list_integrity(list);
    val = 4;
    assert!(list_remove_data(&mut list, int_equal, &mut val) == 4);
    assert!(list_length(list) == (num_entries - 5) as _);
    check_list_integrity(list);
    val = 89;
    assert!(list_remove_data(&mut list, int_equal, &mut val) == 1);
    assert!(list_length(list) == (num_entries - 6) as _);
    check_list_integrity(list);
    list_free(list);
}
#[test]
fn test_list_sort() {
    let entries = vec![89, 4, 23, 42, 4, 16, 15, 4, 8, 99, 50, 30, 4];
    let sorted = vec![4, 4, 4, 4, 8, 15, 16, 23, 30, 42, 50, 89, 99];
    let num_entries = entries.len();
    let mut list = None;
    for entry in &entries {
        assert!(list_prepend(&mut list, entry).is_some());
    }
    list_sort(&mut list, int_compare);
    assert!(list_length(&list) == num_entries);
    for (i, &sorted_value) in sorted.iter().enumerate() {
        let value = list_nth_data(&list, i);
        assert!(*value == sorted_value);
    }
    list_free(list);
    list = None;
    list_sort(&mut list, int_compare);
    assert!(list.is_none());
}
#[test]
fn test_list_find_data() {
    let entries = [89, 23, 42, 16, 15, 4, 8, 99, 50, 30];
    let num_entries = entries.len();
    let mut list: Option<Box<ListEntry>> = None;
    for i in 0..num_entries {
        assert!(list_append(&mut list, &entries[i]).is_some());
    }
    for i in 0..num_entries {
        let val = entries[i];
        let result = list_find_data(&list, int_equal, &val);
        assert!(result.is_some());
        let data = list_data(result.unwrap());
        assert!(*data == val);
    }
    let mut val = 0;
    assert!(list_find_data(&list, int_equal, &val).is_none());
    val = 56;
    assert!(list_find_data(&list, int_equal, &val).is_none());
    list_free(list);
}
#[test]
fn test_list_to_array() {
    let list = generate_list();
    let array;
    unsafe {
        array = list_to_array(list);
        assert!(*array.offset(0) == std::ptr::addr_of!(VARIABLE1));
        assert!(*array.offset(1) == std::ptr::addr_of!(VARIABLE2));
        assert!(*array.offset(2) == std::ptr::addr_of!(VARIABLE3));
        assert!(*array.offset(3) == std::ptr::addr_of!(VARIABLE4));
        Box::from_raw(array);
        alloc_test_set_limit(0);
        let array = list_to_array(list);
        assert!(array.is_null());
        if !list.is_null() {
            Box::from_raw(list);
        }
    }
}
#[test]
fn test_list_iterate() {
    let mut list: *mut ListEntry = ptr::null_mut();
    let mut iter: ListIterator = unsafe { std::mem::zeroed() };
    let mut i: i32;
    let mut a: i32 = 0;
    let mut counter: i32 = 0;
    let mut data: *mut i32;
    for i in 0..50 {
        assert!(!unsafe { list_prepend(&mut list, &mut a) }.is_null());
    }
    counter = 0;
    unsafe {
        list_iterate(&mut list, &mut iter);
        list_iter_remove(&mut iter);
        while list_iter_has_more(&mut iter) {
            data = list_iter_next(&mut iter);
            counter += 1;
            if (counter % 2) == 0 {
                list_iter_remove(&mut iter);
                list_iter_remove(&mut iter);
            }
        }
        assert!(list_iter_next(&mut iter).is_null());
        list_iter_remove(&mut iter);
    }
    assert!(counter == 50);
    assert!(unsafe { list_length(list) } == 25);
    unsafe {
        list_free(list);
    }
    list = ptr::null_mut();
    counter = 0;
    unsafe {
        list_iterate(&mut list, &mut iter);
        while list_iter_has_more(&mut iter) {
            data = list_iter_next(&mut iter);
            counter += 1;
        }
    }
    assert!(counter == 0);
}
#[test]
fn test_list_iterate_bad_remove() {
    let mut list: Option<Box<ListEntry>> = None;
    let mut iter = ListIterator {
        current: None,
        next: None,
    };
    let mut values = [0; 49];
    for i in 0..49 {
        values[i] = i as i32;
        assert!(!list_prepend(&mut list, &mut values[i]).is_null());
    }
    list_iterate(&mut list, &mut iter);
    while iter.has_more() {
        let val = iter.next();
        if unsafe { *val % 2 } == 0 {
            assert!(list_remove_data(&mut list, val) != 0);
            iter.remove();
        }
    }
    list_free(list);
}
fn main() {
    run_tests(&tests);
    println!("num_assert: {}", num_assert);
}
